{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# How to create and control loops\n",
    "\n",
    "<div class=\"admonition tip\">\n",
    "    <p class=\"admonition-title\">Prerequisites</p>\n",
    "    <p>\n",
    "        This guide assumes familiarity with the following:\n",
    "        <ul>\n",
    "            <li>\n",
    "                <a href=\"/langgraphjs/concepts/low_level/#graphs\">\n",
    "                    Graphs\n",
    "                </a>\n",
    "            </li>\n",
    "            <li>\n",
    "                <a href=\"/langgraphjs/concepts/low_level/#recursion-limit\">\n",
    "                    Recursion Limit\n",
    "                </a>\n",
    "            </li>\n",
    "            <li>\n",
    "                <a href=\"/langgraphjs/concepts/low_level/#nodes\">\n",
    "                    Nodes\n",
    "                </a>\n",
    "            </li>\n",
    "        </ul>\n",
    "    </p>\n",
    "</div> \n",
    "\n",
    "\n",
    "When creating a graph with a loop, we require a mechanism for terminating execution. This is most commonly done by adding a [conditional edge](/langgraphjs/concepts/low_level/#conditional-edges) that routes to the END node once we reach some termination condition.\n",
    "\n",
    "You can also set the graph recursion limit when invoking or streaming the graph. The recursion limit sets the number of [supersteps](/langgraphjs/concepts/low_level/#graphs) that the graph is allowed to execute before it raises an error. Read more about the concept of recursion limits [here](/langgraphjs/concepts/low_level/#recursion-limit). \n",
    "\n",
    "Let's consider a simple graph with a loop to better understand how these mechanisms work.\n",
    "\n",
    "## Summary\n",
    "\n",
    "When creating a loop, you can include a conditional edge that specifies a termination condition:\n",
    "\n",
    "```ts\n",
    "const route = async function (state: typeof StateAnnotation.State) {\n",
    "  if (terminationCondition(state)) {\n",
    "    return \"__END__\";\n",
    "  } else {\n",
    "    return \"a\";\n",
    "  }\n",
    "}\n",
    "\n",
    "const graph = StateGraph(State)\n",
    "  .addNode(a)\n",
    "  .addNode(b)\n",
    "  .addConditionalEdges(\"a\", route)\n",
    "  .addEdge(\"b\", \"a\")\n",
    "  .compile();\n",
    "```\n",
    "\n",
    "To control the recursion limit, specify `\"recursionLimit\"` in the config. This will raise a `GraphRecursionError`, which you can catch and handle:\n",
    "\n",
    "```ts\n",
    "import { GraphRecursionError } from \"@langchain/langgraph\";\n",
    "\n",
    "try {\n",
    "  await graph.invoke(inputs, { recursionLimit: 3 });\n",
    "} catch (error) {\n",
    "  if (error instanceof GraphRecursionError) {\n",
    "    console.log(\"Recursion Error\");\n",
    "  } else {\n",
    "    throw error;\n",
    "  }\n",
    "}\n",
    "```\n",
    "\n",
    "## Setup\n",
    "\n",
    "First, let's install the required packages\n",
    "\n",
    "```bash\n",
    "npm install @langchain/langgraph @langchain/core\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div class=\"admonition tip\">\n",
    "    <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
    "    <p style=\"padding-top: 5px;\">\n",
    "        Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
    "    </p>\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Define the graph\n",
    "\n",
    "Let's define a graph with a simple loop. Note that we use a conditional edge to implement a termination condition."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "import { StateGraph, Annotation } from \"@langchain/langgraph\";\n",
    "\n",
    "// Define the state with a reducer\n",
    "const StateAnnotation = Annotation.Root({\n",
    "  aggregate: Annotation<string[]>({\n",
    "    reducer: (a, b) => a.concat(b),\n",
    "    default: () => [],\n",
    "  }),\n",
    "});\n",
    "\n",
    "// Define nodes\n",
    "const a = async function (state: typeof StateAnnotation.State) {\n",
    "  console.log(`Node A sees ${state.aggregate}`);\n",
    "  return { aggregate: [\"A\"] };\n",
    "}\n",
    "\n",
    "const b = async function (state: typeof StateAnnotation.State) {\n",
    "  console.log(`Node B sees ${state.aggregate}`);\n",
    "  return { aggregate: [\"B\"] };\n",
    "}\n",
    "\n",
    "// Define edges\n",
    "const route = async function (state: typeof StateAnnotation.State) {\n",
    "  if (state.aggregate.length < 7) {\n",
    "    return \"b\";\n",
    "  } else {\n",
    "    return \"__end__\";\n",
    "  }\n",
    "}\n",
    "\n",
    "\n",
    "// Define the graph\n",
    "const graph = new StateGraph(StateAnnotation)\n",
    "  .addNode(\"a\", a)\n",
    "  .addNode(\"b\", b)\n",
    "  .addEdge(\"__start__\", \"a\")\n",
    "  .addConditionalEdges(\"a\", route)\n",
    "  .addEdge(\"b\", \"a\")\n",
    "  .compile();"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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"
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import * as tslab from \"tslab\";\n",
    "\n",
    "const drawableGraph = graph.getGraph();\n",
    "const image = await drawableGraph.drawMermaidPng();\n",
    "const arrayBuffer = await image.arrayBuffer();\n",
    "\n",
    "await tslab.display.png(new Uint8Array(arrayBuffer));"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This architecture is similar to a [ReAct agent](/langgraphjs/how-tos/#prebuilt-react-agent) in which node `\"a\"` is a tool-calling model, and node `\"b\"` represents the tools.\n",
    "\n",
    "In our `route` conditional edge, we specify that we should end after the `\"aggregate\"` list in the state passes a threshold length.\n",
    "\n",
    "Invoking the graph, we see that we alternate between nodes `\"a\"` and `\"b\"` before terminating once we reach the termination condition."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Node A sees \n",
      "Node B sees A\n",
      "Node A sees A,B\n",
      "Node B sees A,B,A\n",
      "Node A sees A,B,A,B\n",
      "Node B sees A,B,A,B,A\n",
      "Node A sees A,B,A,B,A,B\n",
      "{\n",
      "  aggregate: [\n",
      "    'A', 'B', 'A',\n",
      "    'B', 'A', 'B',\n",
      "    'A'\n",
      "  ]\n",
      "}\n"
     ]
    }
   ],
   "source": [
    "await graph.invoke({ aggregate: [] });"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Impose a recursion limit\n",
    "\n",
    "In some applications, we may not have a guarantee that we will reach a given termination condition. In these cases, we can set the graph's [recursion limit](/langgraphjs/concepts/low_level/#recursion-limit). This will raise a `GraphRecursionError` after a given number of [supersteps](/langgraphjs/concepts/low_level/#graphs). We can then catch and handle this exception:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Node A sees \n",
      "Node B sees A\n",
      "Node A sees A,B\n",
      "Node B sees A,B,A\n",
      "Recursion Error\n"
     ]
    }
   ],
   "source": [
    "import { GraphRecursionError } from \"@langchain/langgraph\";\n",
    "\n",
    "try {\n",
    "  await graph.invoke({ aggregate: [] }, { recursionLimit: 4 });\n",
    "} catch (error) {\n",
    "  if (error instanceof GraphRecursionError) {\n",
    "    console.log(\"Recursion Error\");\n",
    "  } else {\n",
    "    throw error;\n",
    "  }\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note that this time we terminate after the fourth step."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Loops with branches\n",
    "\n",
    "To better understand how the recursion limit works, let's consider a more complex example. Below we implement a loop, but one step fans out into two nodes:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "import { StateGraph, Annotation } from \"@langchain/langgraph\";\n",
    "\n",
    "// Define the state with a reducer\n",
    "const StateAnnotationWithLoops = Annotation.Root({\n",
    "  aggregate: Annotation<string[]>({\n",
    "    reducer: (a, b) => a.concat(b),\n",
    "    default: () => [],\n",
    "  }),\n",
    "});\n",
    "\n",
    "// Define nodes\n",
    "const nodeA = async function (state: typeof StateAnnotationWithLoops.State) {\n",
    "  console.log(`Node A sees ${state.aggregate}`);\n",
    "  return { aggregate: [\"A\"] };\n",
    "}\n",
    "\n",
    "const nodeB = async function (state: typeof StateAnnotationWithLoops.State) {\n",
    "  console.log(`Node B sees ${state.aggregate}`);\n",
    "  return { aggregate: [\"B\"] };\n",
    "}\n",
    "\n",
    "const nodeC = async function (state: typeof StateAnnotationWithLoops.State) {\n",
    "  console.log(`Node C sees ${state.aggregate}`);\n",
    "  return { aggregate: [\"C\"] };\n",
    "}\n",
    "\n",
    "const nodeD = async function (state: typeof StateAnnotationWithLoops.State) {\n",
    "  console.log(`Node D sees ${state.aggregate}`);\n",
    "  return { aggregate: [\"D\"] };\n",
    "}\n",
    "\n",
    "// Define edges\n",
    "const loopRouter = async function (state: typeof StateAnnotationWithLoops.State) {\n",
    "  if (state.aggregate.length < 7) {\n",
    "    return \"b\";\n",
    "  } else {\n",
    "    return \"__end__\";\n",
    "  }\n",
    "}\n",
    "\n",
    "// Define the graph\n",
    "const graphWithLoops = new StateGraph(StateAnnotationWithLoops)\n",
    "  .addNode(\"a\", nodeA)\n",
    "  .addNode(\"b\", nodeB)\n",
    "  .addNode(\"c\", nodeC)\n",
    "  .addNode(\"d\", nodeD)\n",
    "  .addEdge(\"__start__\", \"a\")\n",
    "  .addConditionalEdges(\"a\", loopRouter)\n",
    "  .addEdge(\"b\", \"c\")\n",
    "  .addEdge(\"b\", \"d\")\n",
    "  .addEdge([\"c\", \"d\"], \"a\")\n",
    "  .compile();"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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FkbtpGbB5HNzDbdbCgIiIKvpHbG5bP4Ruwgq2G2qzB9xFOC4t/JIJth3AcoHcrQqxgB4XrPVF1u/ZsFWgCRsC6NjpHbekfqgDf0gjzKzr0Y/337I8IumdYiIvOgREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBVzK1J8Jl35yjA+1FNG2LIVIhvI9jd+SWNv3T28xBb3ubttuWNa6xor0V6EphW8zhsPxKwcDPbK+aTZu1bPhMtYoyFwBaWukryMcQOY7sJ23A3G4G1ePAjT52/sxrTp/wDeuX/80rRk9FYvJ233Ayejef5VrH2H15H+bxiwgP8A84Fch0ROAA3VGeaB5u3iP8pjJWujhTqqtvj0/wCGSMxfBrB4jJVb0OU1ZLNXkbKxlrVuUnicQdwHxyWHMe30tcCD5wr2qv7ibH7qs9/HQ/ok9xNj91We/jof0Se7w+v+JLR0rQiyviRj8rpXFYmxR1TmDJazWOoSdtLCR2U9qOKTb4MeNyvO3r26HuVs9xNj91We/jof0Se7w+v+JLR0pLU2mqurMW7H3J8hWhc8PMmMyE9Gbcd3wsD2PA9I32PnVRHArT4/+caz/wDzTL/+aU57ibH7qs9/HQ/ok9xNj91We/jof0Se7w+v+JLR0oSPgZgIpGvGX1kS0ggO1nlyPyg2dirPmNRGKy7F4vsrmac3cQuJLK4I6STEeS30DoXdw85HJ7hGTDlt53OXIyNjG68Yg7r5zEGH+VTWJw1HBVBVx9SKnBzF5ZCwN5nHvcfS4+cnqfOn06M76U/j+d3eZQ/GCw0WBxkVOJzpOUufJK/ypZHOLnvd63OJJ+lSCIsaqpqmap1ygREVQREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREGfcatvaDT2+/wBs+G8kb/3fD6x/69PctBWfca2l2A08ACf7Z8MfFbzf3fD/AOt/N3rQUBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREGe8bOX2g09zcu3unwvlb7b+Hw7d3n/k9PRaEs+41AuwOnuVvMfdPhjt17vD4dz0//AJ6ei0FAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBF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"
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import * as tslab from \"tslab\";\n",
    "\n",
    "const drawableGraphWithLoops = graphWithLoops.getGraph();\n",
    "const imageWithLoops = await drawableGraphWithLoops.drawMermaidPng();\n",
    "const arrayBufferWithLoops = await imageWithLoops.arrayBuffer();\n",
    "\n",
    "await tslab.display.png(new Uint8Array(arrayBufferWithLoops));"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This graph looks complex, but can be conceptualized as loop of [supersteps](../../concepts/low_level/#graphs):\n",
    "\n",
    "1. Node A\n",
    "2. Node B\n",
    "3. Nodes C and D\n",
    "4. Node A\n",
    "5. ...\n",
    "\n",
    "We have a loop of four supersteps, where nodes C and D are executed concurrently.\n",
    "\n",
    "Invoking the graph as before, we see that we complete two full \"laps\" before hitting the termination condition:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Node A sees \n",
      "Node B sees A\n",
      "Node C sees A,B\n",
      "Node D sees A,B\n",
      "Node A sees A,B,C,D\n",
      "Node B sees A,B,C,D,A\n",
      "Node C sees A,B,C,D,A,B\n",
      "Node D sees A,B,C,D,A,B\n",
      "Node A sees A,B,C,D,A,B,C,D\n",
      "{\n",
      "  aggregate: [\n",
      "    'A', 'B', 'C',\n",
      "    'D', 'A', 'B',\n",
      "    'C', 'D', 'A'\n",
      "  ]\n",
      "}\n"
     ]
    }
   ],
   "source": [
    "await graphWithLoops.invoke({ aggregate: [] })"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "However, if we set the recursion limit to four, we only complete one lap because each lap is four supersteps:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Node A sees \n",
      "Node B sees A\n",
      "Node C sees A,B\n",
      "Node D sees A,B\n",
      "Node A sees A,B,C,D\n",
      "Recursion Error\n"
     ]
    }
   ],
   "source": [
    "import { GraphRecursionError } from \"@langchain/langgraph\";\n",
    "\n",
    "try {\n",
    "  await graphWithLoops.invoke({ aggregate: [] }, { recursionLimit: 4 });\n",
    "} catch (error) {\n",
    "  if (error instanceof GraphRecursionError) {\n",
    "    console.log(\"Recursion Error\");\n",
    "  } else {\n",
    "    throw error;\n",
    "  }\n",
    "}"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "TypeScript",
   "language": "typescript",
   "name": "tslab"
  },
  "language_info": {
   "codemirror_mode": {
    "mode": "typescript",
    "name": "javascript",
    "typescript": true
   },
   "file_extension": ".ts",
   "mimetype": "text/typescript",
   "name": "typescript",
   "version": "3.7.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
